from typing import Any, Callable, Optional
from gi.repository import Gtk, Gio, GtkSource, GObject


KEYWORDS = ["SELECT", "FROM", "WHERE", "INSERT INTO", "UPDATE", "DELETE FROM"]


class Keyword(GObject.Object, GtkSource.CompletionProposal):
    __gtype_name__ = "Keyword"

    value = GObject.Property(type=str)

    def __init__(self, value: str):
        GObject.Object.__init__(self)
        self.value = value

    def __eq__(self, other: "Keyword"):
        return self.value == other.value.upper()


class Text:
    value: str


class SQLCompletionProvider(GObject.GObject, GtkSource.CompletionProvider):
    def __init__(self):
        super().__init__()
        print("SQLCompletionProvider...")
        self._priority = 2000

        self._proposals = Gio.ListStore.new(Keyword)
        for keyword in KEYWORDS:
            self._proposals.append(Keyword(keyword))
        self._filter_data = ""

    def do_get_priority(self, context: GtkSource.CompletionContext) -> int:
        print("get_priority...")
        return self._priority

    def do_get_title(self) -> str:
        print("get_title...")
        return self.__class__.__name__

    def do_display(
        self,
        context: GtkSource.CompletionContext,
        proposal: GtkSource.CompletionProposal,
        cell: GtkSource.CompletionCell,
    ) -> None:
        print("do_display...")
        if cell.props.column == GtkSource.CompletionColumn.AFTER:
            pass
        elif cell.props.column == GtkSource.CompletionColumn.TYPED_TEXT:
            print("proposal:", proposal.value)
            cell.set_text(proposal.value)

    def do_activate(
        self,
        context: GtkSource.CompletionContext,
        proposal: GtkSource.CompletionProposal,
    ) -> None:
        print("do_activate...")
        buffer = context.get_buffer()
        buffer.begin_user_action()
        has_selection, begin, end = context.get_bounds()
        if has_selection:
            buffer.delete(begin, end)

        text = proposal.value
        buffer.insert(begin, text, len(text))
        buffer.end_user_action()

    def do_populate_async(
        self,
        context: GtkSource.CompletionContext,
        cancellable: Optional[Gio.Cancellable] = None,
        callback: Optional[Callable[..., None]] = None,
        *user_data: Any,
    ) -> None:
        print("do_populate_async...")

        def filter_fn(proposal: Keyword, data: GtkSource.CompletionContext) -> bool:
            value = data.get_word()
            matched = value and proposal.value.startswith(value.upper())
            if matched:
                print("matched:", proposal.value)
            return matched

        store_filter = Gtk.CustomFilter.new(filter_fn, context)
        proposals = Gtk.FilterListModel.new(self._proposals, store_filter)
        context.set_proposals_for_provider(self, proposals)

    def do_refilter(self, context: GtkSource.CompletionContext, model: Gio.ListModel) -> None:
        word = context.get_word()
        print("refilter...")
        change = Gtk.FilterChange.DIFFERENT
        if old_word := self._filter_data:
            if word.startswith(old_word):
                change = Gtk.FilterChange.MORE_STRICT
            elif old_word.startswith(word):
                change = Gtk.FilterChange.LESS_STRICT
        self._filter_data = word
        model.get_filter().changed(change)
